#include "EpollEventLoop.hpp"
#include "Debug.hpp"

namespace se {

EpollEventLoop::EpollEventLoop(Acceptor &acceptor) : _loopFlag(false), _acceptor(acceptor), _epollEventList(1024) {
    this->_epollFileDescriptor = ::epoll_create1(0);
    this->addFileDescriptor(this->_acceptor.getFileDescriptor());
}

void EpollEventLoop::start() { 
    this->_loopFlag = true;
    int readyCount = 0;
    while (this->_loopFlag) {
        readyCount = ::epoll_wait(this->_epollFileDescriptor, &this->_epollEventList[0], this->_epollEventList.capacity(), 5000);
        // DEBUG_PRINT("readyCount = %d", readyCount);
        if (readyCount == -1 && errno == EINTR) {
            continue;
        } else if (readyCount == -1) {
            ::perror("epoll_wait");
            break;
        } else if (readyCount == 0) {
            // DEBUG_PRINT("epoll_wait timeout.");
        } else {
            for (int i = 0; i < readyCount; ++i) {
                if (this->_epollEventList[i].data.fd == this->_acceptor.getFileDescriptor()) {
                    this->handlerNewTcpConnection();
                } else {
                    this->handlerTcpMessage(this->_epollEventList[i].data.fd);
                }
            }
        }
    }
}

void EpollEventLoop::handlerNewTcpConnection() {
    int cilentFileDescriptor = this->_acceptor.accept();
    std::shared_ptr<TcpConnection> newTcpConnection(new TcpConnection(cilentFileDescriptor));

    newTcpConnection->OnConnected = this->OnTcpConnected;
    newTcpConnection->OnMessageReceived = this->OnTcpMessageReceived;
    newTcpConnection->OnClosed = this->OnTcpClosed;

    // 当TCP连接断开时，需要从监听队列中删除该TCP连接对应的文件描述符
    newTcpConnection->OnClosed += [this](std::shared_ptr<TcpConnection> tcpConnection) {
        this->_tcpConnections.erase(tcpConnection->getFileDescriptor());
        this->deleteFileDescriptor(tcpConnection->getFileDescriptor());
    };

    this->_tcpConnections.insert(std::make_pair(cilentFileDescriptor, newTcpConnection));
    this->addFileDescriptor(cilentFileDescriptor);

    newTcpConnection->OnConnected(newTcpConnection);
}

void EpollEventLoop::handlerTcpMessage(int clientFileDescription) {
    auto it = this->_tcpConnections.find(clientFileDescription);
    if (it != this->_tcpConnections.end()) {
        it->second->OnMessageReceived.invoke(it->second);
    }
}

void EpollEventLoop::addFileDescriptor(int fileDescriptor) {
    struct epoll_event event;
    event.data.fd = fileDescriptor;
    event.events = EPOLLIN;
    if (::epoll_ctl(this->_epollFileDescriptor, EPOLL_CTL_ADD, fileDescriptor, &event)) {
        ::perror("epoll_ctl");
    }
}

void EpollEventLoop::deleteFileDescriptor(int fileDescriptor) {
    struct epoll_event event;
    event.data.fd = fileDescriptor;
    ::epoll_ctl(this->_epollFileDescriptor, EPOLL_CTL_DEL, fileDescriptor, &event);
}

}  // namespace se